
local slot = require[[slot]]

local mt = {}
mt.__index = mt

mt.new = function(initial)
	if mt._io == nil then error("you must install at least one io") end
	local this = setmetatable({
		on_complete = slot.new(),
		_value = initial,
	}, mt)
	this.on_change = slot.new(function(listener)	-- agument is always (x)
		listener(this:get())
	end)
	return this
end

mt.from_stream = function(stream, initial)
	local s = mt.new(initial)
	stream.on_event(function(_, x)
		s:set(x)
	end)
	return s
end

mt.set = function(this, x)
	if this._value == x then return end
	this._value = x
	this.on_change:put(x)
end

mt.get = function(this)
	return this._value
end

mt.sample = function(this, duration)
	local s = require[[stream]].new()
	local make_timed_out
	make_timed_out = function()
		return function(t)
			s.on_event:put(t, this:get())
			mt._io.timeout(make_timed_out(this, s), duration)
		end
	end
	mt._io.timeout(make_timed_out(this, s), duration)
	return s
end

mt.sample_on_change = function(this)
	local s = require[[stream]].new()
	this.on_change(function(x) s.on_event:put(mt._io.now(), x) end)
	return s
end

return mt

